﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Xml;

namespace Engine.Sgml.Decoding {
    [StructLayout(LayoutKind.Auto)]
    internal struct DetectHelper {
        private char[] buffer;
        private int index;
        private int charsCount;

        public DetectHelper(char[] buffer, int charsCount) {
            this.buffer = buffer;
            this.index = 0;
            this.charsCount = charsCount;
            }

        public string SniffAttribute(string name) {
            SniffWhitespace();
            if (string.Equals(name, SniffName(), StringComparison.InvariantCultureIgnoreCase)) {
                SniffWhitespace();
                if (SniffPattern("=")) {
                    SniffWhitespace();
                    return SniffLiteral();
                    }
                }
            return null;
            }

        public string SniffAttribute(out string name) {
            SniffWhitespace();
            name = SniffName();
            if (name != null) {
                SniffWhitespace();
                if (SniffPattern("=")) {
                    SniffWhitespace();
                    return SniffLiteral();
                    }
                }
            return null;
            }

        public bool SniffPattern(string pattern) {
            int ch = PeekBuffer();
            if (ch != pattern[0])
                return false;
            for (int i = 0, l = pattern.Length; ch != SgmlStream.EndOfFile && i < l; i++) {
                ch = ReadBuffer();
                if (ch != pattern[i])
                    return false;
                }
            return true;
            }

        public string SniffName() {
            int c = PeekBuffer();
            if (c == SgmlStream.EndOfFile)
                return null;
            int start = index;
            char ch = buffer[index];
        scan:
            if (XmlConvert.IsStartNCNameChar(ch)) {
                if (++index < charsCount) {
                    ch = buffer[index];
                    while (XmlConvert.IsNCNameChar(ch) && ++index < charsCount) {
                        ch = buffer[index];
                        }
                    if (ch == Characters.EndNamePrefix && ++index < charsCount)
                        goto scan;
                    }
                }
            if (start == index)
                return null;
            else
                return new string(buffer, start, index - start);
            }


        public void SniffTerminator(string terminators) {
            int ch = ReadBuffer();
            int termIndex = 0;
            int termLength = terminators.Length;
            while (termIndex < termLength && ch != SgmlStream.EndOfFile) {
                if (terminators[termIndex] == ch) {
                    termIndex++;
                    if (termIndex == termLength)
                        break;
                    }
                else
                    termIndex = 0;
                ch = ReadBuffer();
                }
            }

        public int ReadBuffer() {
            if (index < charsCount)
                return buffer[index++];
            else
                return SgmlStream.EndOfFile;
            }

        private char PeekBuffer() {
            if (index < charsCount)
                return buffer[index];
            else
                return Characters.EndOfFile;
            }

        private string SniffLiteral() {
            int quoteChar = PeekBuffer();
            if (quoteChar == Characters.Apostrophe || quoteChar == Characters.QuotationMark) {
                // Consume quote char
                ReadBuffer();
                int i = index;
                int ch = ReadBuffer();
                while (ch != SgmlStream.EndOfFile && ch != quoteChar) {
                    ch = ReadBuffer();
                    }
                if (index > i)
                    return new string(buffer, i, index - i - 1);
                else
                    return string.Empty;
                }
            return null;
            }

        private void SniffWhitespace() {
            char ch = (char)PeekBuffer();
            int i = index;
            while (ch == Characters.Whitespace ||
                ch == Characters.HorizontalTab ||
                ch == Characters.CarriageReturn ||
                ch == Characters.LineFeed) {
                i = index;
                ch = (char)ReadBuffer();
                }
            index = i;
            }
        }
    }
